/*
* @Author: ystlong
* @Date:   2018-06-30 10:25:53
* @Last Modified by:   ystlong
* @Last Modified time: 2018-06-30 17:21:02
*/

#include <stdio.h>
#include <stdlib.h>
#include "bfd.h"

#define _(msg) "%s:%d " msg, __FILE__, __LINE__

#define debug(msg) printf(_(msg));

static int exit_status = 0;

#define nonfatal(msg)    \
  do {                   \
    printf(_(""));       \
    printf("%s\n", msg); \
    exit_status = 1;     \
  } while (0)

static long get_file_size(const char *filename) {
  struct stat st;
  stat(filename, &st);
  return st.st_size;
}

static bfd *open_file(const char *filename) {
  bfd *file;
  char *target = NULL;
  // target = "elf64-x86-64";
  if (get_file_size(filename) < 1) {
    exit_status = 1;
    return NULL;
  }

  file = bfd_openr(filename, target);

  if (file == NULL) {
    // nonfatal(filename);
    printf(_("bfd file openr error%s\n"), filename);
    return NULL;
  }
  return file;
}

static info_asection(asection *sect) {
  printf("sect name: %-20s, ", sect->name);
  printf("size: %-8x, ", sect->size);
  printf("filepos: %-8x,", sect->filepos);
  // printf("file_ptr origin: %-8x, ", sect->origin);
  // printf("offset: %-8d, ", sect->offset);
  printf("vma: 0x%x", sect->vma);
  printf("\n");
}

static void sec_func_call(bfd *abfd, asection *sect, void *obj) {
  info_asection(sect);
}

static void read_section_data(long file_off, size_t data_size) {
  FILE *file = fopen("main.elf", "r");
  if (file == NULL) {
    perror("open file erro");
    return;
  }
  fseek(file, file_off, SEEK_SET);
  bfd_byte data[1024];
  size_t len = data_size > 1024 ? 1024 : data_size;
  fread(data, len, 1, file);
  for (int i = 0; i < len; i++) {
    printf("%x", data[i]);
  }
  printf("\n");
  fclose(file);
}

static void read_section(bfd *abfd, asection *section) {
  // bfd_boolean bfd_get_section_contents
  //   (bfd *abfd, asection *section, void *location, file_ptr offset,
  //    bfd_size_type count);

  // bfd_boolean bfd_malloc_and_get_section
  // (bfd *abfd, asection *section, bfd_byte **buf);

  bfd_byte *buf = NULL;
  if (bfd_malloc_and_get_section(abfd, section, &buf)) {
    for (int i = 0; i < section->size; i++) {
      // printf("%d, ", i);
      printf("%x", buf[i]);
    }
    buf[0] = 0xc3;
    printf("\n");
  }
  read_section_data(section->filepos + abfd->origin, section->size);

  //   bfd_get_section_contents(abfd, section, buf, section->filepos,
  //   section->size);
  //   int d=0;
  //   for(int i=0; i<section->size; i++){
  //   		d = buf[i];
  // 	printf("%2x", d);
  //   }
  // printf("\n");
}

static void get_section(bfd *abfd) {
  // asection *bfd_get_section_by_name (bfd *abfd, const char *name);
  debug("========\n");
  if (abfd == NULL) debug("----\n");
  bfd_map_over_sections(abfd, sec_func_call, NULL);
  asection *section = bfd_get_section_by_name(abfd, ".text");
  // asection *section = bfd_get_section_by_name(abfd, ".plt.got");
  if (section == NULL) {
    debug("---\n");
  } else {
    printf("section name: %s\n", section->name);
    read_section(abfd, section);
    // read_section_data(section->filepos);
  }
}

static void info_symbol(asymbol *symbol) {
	printf("symbol ");
	printf("name: %-15s, ", symbol->name);
	printf("value: %-8x, ", symbol->value);
	printf("\n");

	if(strcmp(symbol->name, "main") == 0) {
		info_asection(symbol->section);
	}
	return;
}

static void symbol_read(bfd *abfd) {
  long storage_needed;
  asymbol **symbol_table;
  long number_of_symbols;
  long i;

  storage_needed = bfd_get_symtab_upper_bound(abfd);

  if (storage_needed < 0) {
    printf(_("get sytab upper bound error\n"));
    return;
  }
  if (storage_needed == 0) return;

  symbol_table = xmalloc(storage_needed);
  // ...
  number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table);

  if (number_of_symbols < 0) {
    printf(_("get sytab upper bound error\n"));
    return;
  }
  for (i = 0; i < number_of_symbols; i++) {
    info_symbol(symbol_table[i]);
  }
}

unsigned int number_of_sections(bfd *abfd)
// bfd *abfd;
{
  return bfd_count_sections(abfd);
}

static void target_list() {
  char **a = bfd_target_list();
  for (; *a; a++) {
    printf("%s\n", *a);
  }
}

static void info_bfd(bfd *abfd) {
  printf("bfd name: %s, ", abfd->filename);
  printf("sections: %p, ", abfd->sections);
  printf("start_address: %x, ", abfd->start_address);
  printf("target_xvec_name: %s, ", abfd->xvec->name);
  printf("origin: %-8x, ", abfd->origin);
  printf("proxy_origin: %-8x, ", abfd->proxy_origin);
  printf("\n");
}

static void display_object_bfd(bfd *abfd) {
  debug("bfd objdect\n");
  // bfd_check_format_matches(abfd, bfd_object, &matching);

  // if(!bfd_make_writable(abfd))
  // {
  // 	printf(_("make bfd_make_writable faild\n"));
  // 	return 2;
  // }
  printf("number of sections = %d\n", bfd_count_sections(abfd));
  // target_list();
  //
  char **matching;
  if (bfd_check_format_matches(abfd, bfd_object, &matching)) {
    // dump_bfd (abfd);
    info_bfd(abfd);
    get_section(abfd);

    symbol_read(abfd);
    return;
  }

  // if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
  //   {
  //     nonfatal (bfd_get_filename (abfd));
  //     // list_matching_formats (matching);
  //     free (matching);
  //     return;
  //   }

  if (bfd_get_error() != bfd_error_file_not_recognized) {
    nonfatal(bfd_get_filename(abfd));
    return;
  }

  // info_bfd(abfd);
  // get_section(abfd);
}

static void display_any_bfd(bfd *abfd, int level) {
  char **matching;
  if (bfd_check_format_matches(abfd, bfd_archive, &matching)) {
    debug("bfd archive\n");
    // return 0;
    bfd *arfile = NULL;
    bfd *last_arfile = NULL;

    if (level == 0)
      printf(_("In archive %s:\n"), bfd_get_filename(abfd));
    else if (level > 100) {
      printf(_("Archive nesting is too deep"));
      return;
    } else
      printf(_("In nested archive %s:\n"), bfd_get_filename(abfd));

    for (;;) {
      bfd_set_error(bfd_error_no_error);

      arfile = bfd_openr_next_archived_file(abfd, arfile);
      if (arfile == NULL) {
        if (bfd_get_error() != bfd_error_no_more_archived_files)
          nonfatal(bfd_get_filename(abfd));
        break;
      }

      display_any_bfd(arfile, level + 1);

      if (last_arfile != NULL) {
        bfd_close(last_arfile);
        /* PR 17512: file: ac585d01.  */
        if (arfile == last_arfile) {
          last_arfile = NULL;
          break;
        }
      }
      last_arfile = arfile;
    }

    if (last_arfile != NULL) bfd_close(last_arfile);
  } else {
    printf(_("no archive\n"));
    display_object_bfd(abfd);
  }
}

// static void write(bfd *abfd)
// {
// 	bfd *file = bfd_openw(abfd->filename, NULL);
// }

int main() {
  // bfd abfd;
  // abfd.filename = "main.elf";
  int n = 0;

  char *filename = "main.elf";
  char *target = "";
  bfd *abfd = NULL;
  // bfd_init ();
  // set_default_bfd_target ();
  bfd_init();

  abfd = open_file(filename);
  if (abfd == NULL) {
    nonfatal("open file error");
    return 1;
  }

  char **matching;

  display_any_bfd(abfd, 0);
  return 0;

  // if (bfd_check_format_matches(abfd, bfd_archive, &matching)) {
  //   debug("bfd archive\n");
  //   return 0;
  // }
  // debug("bfd objdect\n");
  // bfd_check_format_matches(abfd, bfd_object, &matching);

  // // if(!bfd_make_writable(abfd))
  // // {
  // // 	printf(_("make bfd_make_writable faild\n"));
  // // 	return 2;
  // // }

  // printf("number of sections = %d\n", bfd_count_sections(abfd));

  // // target_list();
  // info_bfd(abfd);
  // get_section(abfd);

  // bfd_close_all_done(abfd);
  bfd_close(abfd);

  // n = number_of_sections(abfd);
  // printf("%d\n", n);

  // display_file(filename, target, TRUE);
  return 0;
}
